#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "../complejo/complejo.h"

// SET(1): Formato P2 PGM Plano
// RESET(0): Formato P5 PGM Normal
#define SET_FORMATO_P2 1

// Resolution
#define WIDTH_DEFAULT 640
#define HEIGHT_DEFAULT 480


typedef struct Resolution{
	int x;
	int y;
} Resolution;

typedef struct Scale{
	float x;
	float y;
} Scale;

void setDefault(char *error, float *width, float *height, Resolution *resolution,
				complejo *center, int *hay_error){
	stringToComplejo("0+0i", center);

	*width = 4.0;
	*height = 4.0;

	resolution->x = WIDTH_DEFAULT;
	resolution->y = HEIGHT_DEFAULT;

	hay_error = 0;
}

void cerrarArchivo(FILE **archivo){
	if(*archivo){
		fclose(*archivo);
		(*archivo) = NULL;
	}
}

//Convierte un string que posee dos números positivos separados por el caracter x
//En una resolución.
//Retorna 0 en caso de formato erróneo, 1 en caso de tener al menos un valor negativo
//y 2 en caso de éxito.
int stringToResolution(char *str, Resolution *resolution){
	if(sscanf(str, "%ix%i", &(resolution->x), &(resolution->y)) < 2)
		return 0;
	if((resolution->x <= 0) || (resolution->y <= 0))
		return 1;
	return 2;
}

void mostrarPorPantalla(Resolution resolution){
	int i, j;
	for(i = 0; i < resolution.y; i++){
		for(j = 0; j < resolution.x; j++){

		}
	}
}

// TODO Corroborar si es correcto que devuelva el struct escala. (por ahora anda)
Scale calcularEscala( Resolution *resolucion, complejo *amplitud ){
	Scale escala;
	// Corrimiento en los ejes por pixel.
	escala.x = (float) amplitud->real / resolucion->x ;
	escala.y = (float) amplitud->imaginaria / resolucion->y;

	return escala;
}

#if SET_FORMATO_P2 == 1
void writePGM(Resolution resolution, FILE* archivo, complejo amplitud, complejo center){
	// Encabezado del Archivo PGM para que lo identifique como tal,
	// junto con la resolucion y el valor máximo del Rango de intensidades. [de 0 a MAX_INTENSIDAD]
	if(archivo != NULL) fprintf(archivo, "P2 %i %i %i\n", resolution.x, resolution.y, MAX_INTENSIDAD);

	// Calculo de la escala entre pixeles y numeros imaginarios
	Scale escala = calcularEscala(&resolution, &amplitud);

	complejo ver_sup_izq, auxiliar;
	// Auxiliar (No es el centro)
	auxiliar.real = 0.0f;
	auxiliar.imaginaria = 0.0f;
	// Vertice superior izquierdo.
	ver_sup_izq.real = center.real - amplitud.real/2;
	ver_sup_izq.imaginaria = center.imaginaria + amplitud.imaginaria/2;

	int intensidad = 0;
	int y, x;

	for( y = 0; y < resolution.y; y++){
		// Calculo de la componente y (parte Imaginaria)
		auxiliar.imaginaria = ver_sup_izq.imaginaria - ( y + 0.5 ) * escala.y;
		for( x = 0; x < resolution.x; x++){
			// Calculo de la componente x (parte Real)
			auxiliar.real = ver_sup_izq.real + ( x + 0.5) * escala.x;

			intensidad = brillo(&auxiliar);
			if(archivo != NULL){
				if (intensidad < 10){
					fprintf(archivo, "   %i", intensidad);
				} else{
					if (intensidad < 100){
						fprintf(archivo, "  %i", intensidad);
					} else{
						if (intensidad >= 100){
							fprintf(archivo, " %i", intensidad);
						}
					}
				}
			} else{
				if (intensidad < 10){
					printf("   %i", intensidad);
				} else{
					if (intensidad < 100){
						printf("  %i", intensidad);
					} else{
						if (intensidad >= 100){
							printf(" %i", intensidad);
						}
					}
				}
			}
		}
		if(archivo != NULL){
			fprintf(archivo, "\n");
		} else{
			printf("\n");
		}
	}
}
#else
void writePGM(Resolution resolution, FILE* archivo  ){

	// Encabezado del Archivo PGM para que lo identifique como tal,
	// junto con la resolucion y el valor máximo del Rango de intensidades. [de 0 a MAX_INTENSIDAD]
	fprintf(archivo, "P5 %i %i %i\n", resolution.x, resolution.y, MAX_INTENSIDAD);

	// Amplitud del plano. TODO [Hay que precalcularla]
	complejo amplitud;
	amplitud.real = 4.0f; //Width of x
	amplitud.imaginaria = 4.0f; //Heigth of y

	// Calculo de la escala entre pixeles y numeros imaginarios
	Scale escala = calcularEscala(&resolution, &amplitud);

	complejo ver_sup_izq, auxiliar;
	// Auxiliar (No es el centro)
	auxiliar.real = 0.0f;
	auxiliar.imaginaria = 0.0f;
	// Vertice superior izquierdo. TODO [Hay que precalcularlo]
	ver_sup_izq.real = -2.0f;
	ver_sup_izq.imaginaria = 2.0f;

	int intensidad = 0;
	int y, x;

	for( y = 0; y < resolution.y; y++){
		//Calculo de la componente y (parte Imaginaria)
		auxiliar.imaginaria = ver_sup_izq.imaginaria - ( y + 0.5 ) * escala.y;
		for( x = 0; x < resolution.x; x++){
			//Calculo de la componente x (parte Real)
			auxiliar.real = ver_sup_izq.real + ( x + 0.5) * escala.x;

			intensidad = brillo(&auxiliar);
			//Guarda la intensidad en el archivo. Se usa unsigned char porque es 1 byte.
			fprintf(archivo, "%c", (unsigned char) intensidad);
		}
	}

}
#endif

void getParametros(char *argv[], int argc, char *error, float *width, float *height,
		Resolution *resolution, complejo *center, FILE **archivo, int *hay_error){

	if (argc > 1){
		int i;
		for (i=1; i < argc; i++){
			if((strcmp(argv[i], "-c") == 0) || (strcmp(argv[i], "--center")) == 0){
				if(((argc <= (i + 1)) || stringToComplejo(argv[i+1], center) < 3)){
					strcpy(error, "fatal: invalid center specification.");
					(*hay_error) = 1;
					break;
				}
				i = i+1;
			}

			if((strcmp(argv[i], "-r") == 0) || (strcmp(argv[i], "--resolution") == 0)){
				if((argc <= (i + 1)) || (stringToResolution(argv[i + 1], resolution) < 2)){
					strcpy(error, "fatal: invalid resolution specification.");
					(*hay_error) = 1;
					break;
				}
				i = i+1;
			}

			if((strcmp(argv[i], "-w") == 0) || (strcmp(argv[i], "--width") == 0)){
				if((argc <= (i + 1)) || (((*width) = atof(argv[i + 1])) <= 0)){
					strcpy(error, "fatal: invalid width specification.");
					(*hay_error) = 1;
					break;
				}
				i = i+1;
			}

			if((strcmp(argv[i], "-H") == 0) || (strcmp(argv[i], "--height") == 0)){
				if((argc <= (i + 1)) || (((*height) = atof(argv[i + 1])) <= 0)){
					strcpy(error, "fatal: invalid height specification.");
					(*hay_error) = 1;
					break;
				}
				i = i+1;
			}
			if((strcmp(argv[i], "-o") == 0) || (strcmp(argv[i], "--output") == 0)){
				if((argc > (i + 1)) && (strcmp(argv[i + 1], "-") == 0)){
					i = i+1;
				} else
					if((argc <= (i + 1)) || (!((*archivo) = fopen(argv[i + 1], "w")))){
						(*archivo) = NULL;
						strcpy(error, "fatal: invalid output specification.");
						(*hay_error) = 1;
						break;
					}
			}
		}
	}
}

int main(int argc, char *argv[]) {
	char error[100];
	float width = 0, height = 0;
	int hay_error = 0;
	Resolution resolution;
	complejo center;
	FILE *archivo = NULL;

	setDefault(error, &width, &height, &resolution, &center, &hay_error);
	getParametros(argv, argc, error, &width, &height, &resolution, &center, &archivo, &hay_error);

	if(hay_error){
		if(archivo){
			cerrarArchivo(&archivo);
		}
		printf("%s\n", error);
		return 1;
	}

	complejo amplitud;
	amplitud.real = width;
	amplitud.imaginaria = height;

	if(!hay_error) writePGM(resolution, archivo, amplitud, center);

	cerrarArchivo(&archivo);

	return 0;
}
